#include <stdio.h>
#include <string.h>

#include "plat_display.h"
#include "file_map.h"

#define DIS_INFO(...) fprintf(stdout, "\033[32m[DISPLAY_INFO] %s(%d): \033[0m", __FUNCTION__, __LINE__),fprintf(stdout, __VA_ARGS__)
#define DIS_ERR(...) fprintf(stderr, "\033[31m[DISPLAY_ERR] %s(%d): \033[0m", __FUNCTION__, __LINE__),fprintf(stderr, __VA_ARGS__)

//fb基本信息
#define FB_DEV "/dev/fb0"
static FbMapStruct *gFbMap = NULL;
static Image gImage = {};
static int32_t gFbUsers = 0; //当前使用人数,当最后一个退出时才正真释放内存

static void FbMapInit()
{
    gFbUsers += 1;

    if (gFbMap)
        return;

    gFbMap = FbMapOpen(FB_DEV);
    if (!gFbMap)
        DIS_ERR("FbMapOpen failed \n");
    else
    {
        gImage.info.width = gFbMap->fbInfo.xres;
        gImage.info.height = gFbMap->fbInfo.yres;
        gImage.info.pb = gFbMap->bpp;
        gImage.info.size = gImage.info.width * gImage.info.height * gImage.info.pb;
        gImage.info.format = IMAGE_FORMAT_BGRA;
        gImage.mem.addr = new uint8_t[gImage.info.size]();

        DIS_INFO("FbMapOpen: %dx%d, %d, %d \n",
            gImage.info.width, gImage.info.height, gImage.info.pb, gImage.info.format);
    }
}

static void FbMapDeInit()
{
    if (!gFbMap)
        return;

    gFbUsers -= 1;
    if (gFbUsers > 0)
        return;

    FbMapClose(gFbMap);
    gFbMap = NULL;

    if (gImage.mem.addr)
    {
        delete[] gImage.mem.addr;
        gImage.mem.addr = NULL;
    }
}

PlatDisplay::PlatDisplay()
{
    FbMapInit();
}

PlatDisplay::~PlatDisplay()
{
    FbMapDeInit();
}

//获取显示器内存和信息
Image PlatDisplay::GetMap()
{
    return gImage;
}

//刷新
void PlatDisplay::Refresh()
{
    if (!gFbMap)
        return;
    
    int32_t srcLineSize = gImage.info.width * gImage.info.pb;
    int32_t distLineSize = gFbMap->fbInfo.xres_virtual * gImage.info.pb;

    uint8_t *srcPtr = gImage.mem.addr;
    uint8_t *distPtr = gFbMap->fb + (gFbMap->fbInfo.yoffset * gFbMap->fbInfo.xres_virtual + gFbMap->fbInfo.xoffset) * gImage.info.pb;

    for (uint32_t i = 0; i < gImage.info.height; i++)
    {
        memcpy(distPtr, srcPtr, srcLineSize);
        srcPtr += srcLineSize;
        distPtr += distLineSize;
    }
}

void PlatDisplay::RefreshRect(int32_t x, int32_t y, uint32_t width, uint32_t height)
{
    if (!gFbMap)
        return;

    if (x < (int32_t)gImage.info.width &&
        y < (int32_t)gImage.info.height &&
        x + (int32_t)width > 0 &&
        y + (int32_t)height > 0)
        ;
    else
        return;

    int32_t xBegin = x < 0 ? 0 : x;
    int32_t yBegin = y < 0 ? 0 : y;

    int32_t xEnd = x + width;
    int32_t yEnd = y + height;

    int32_t xSize = (xEnd > (int32_t)gImage.info.width ? gImage.info.width - xBegin : xEnd - xBegin) * gImage.info.pb;
    int32_t ySize = yEnd > (int32_t)gImage.info.height ? gImage.info.height - yBegin : yEnd - yBegin;

    int32_t usrLineSize = width * gImage.info.pb;
    int32_t disLineSize = gFbMap->fbInfo.xres_virtual * gImage.info.pb;

    uint8_t *usrPtr = gImage.mem.addr + ((yBegin - y) * width + (xBegin - x)) * gImage.info.pb;
    uint8_t *disPtr = gFbMap->fb +
                         ((yBegin + gFbMap->fbInfo.yoffset) * gFbMap->fbInfo.xres_virtual + gFbMap->fbInfo.xoffset + xBegin) * gImage.info.pb;

    for (int32_t i = 0; i < ySize; i++)
    {
        memcpy(disPtr, usrPtr, xSize);
        usrPtr += usrLineSize;
        disPtr += disLineSize;
    }
}
